﻿using System;
namespace Affinity.Core.Messages
{
    public abstract class ChannelReader : IDisposable
    {
        public static ChannelReader Create<TMessage>(IReadableChannel<TMessage> channel, MessageSentHandler<TMessage> messageHandler)
        {
            if (channel == null) throw new ArgumentNullException("channel");
            if (messageHandler == null) throw new ArgumentNullException("messageHandler");

            return new ChannelReader<TMessage>(channel, messageHandler);
        }

        public abstract void Dispose();

        public static bool Match<TMessage>(ChannelReader channelReader, IReadableChannel<TMessage> channel, MessageSentHandler<TMessage> messageHandler = null)
        {
            if (channelReader == null) throw new ArgumentNullException("channelReader");
            if (channel == null) throw new ArgumentNullException("channel");

            return channelReader.Matches(channel, messageHandler);
        }

        public bool Matches<TMessage>(IReadableChannel<TMessage> channel, MessageSentHandler<TMessage> messageHandler = null)
        {
            if (!(this is ChannelReader<TMessage>)) throw new ArgumentException("The specified channel reader does not correspond to the specified channel type.");

            return ((ChannelReader<TMessage>)this).Matches(channel, messageHandler);
        }

        protected ChannelReader() { }
    }

    internal class ChannelReader<TMessage> : ChannelReader
    {
        public ChannelReader(IReadableChannel<TMessage> channel, MessageSentHandler<TMessage> messageHandler)
        {
            _Channel = channel;
            _MessageHandler = messageHandler;

            _Channel.Subscribe(HandleMessage);
        }

        public bool Matches(IReadableChannel<TMessage> channel, MessageSentHandler<TMessage> messageHandler = null)
        {
            return _Channel == channel && (messageHandler == null || messageHandler == _MessageHandler);
        }

        public override void Dispose()
        {
            _Channel.Unsubscribe(HandleMessage);
        }

        private void HandleMessage(TMessage message)
        {
            if (message == null) throw new ArgumentNullException("message");
            _MessageHandler(message);
        }

        private IReadableChannel<TMessage> _Channel = null;
        private MessageSentHandler<TMessage> _MessageHandler = null;
    }
}
